tl;dr

This notebook determines the model framework utilized for this PRD. It utilizes the results from the hyperparameter tuning step, and trains the optimal model for each algorithm on the v67 dataset. The resultant predictions of these optimal models are compared to a v68 validation dataset. The framework used to generate the highest performant model (e.g., features, hyperparameters) will be used in online model training for the subsetting of Beta in production use.

Features

Feature sets:

  1. Only performance metrics.
  2. Performance metrics and the highest found by Boruta with original dataset.
  3. Performance metrics and the highest found by Boruta with equal labels dataset.
  4. Performance metrics and all covariates.
  5. Only the highest covariates found by Boruta (excluding performance).
  6. Only the highest covariates found by Boruta (equal labels, excluding performance).
  7. Utilize all covariates (excluding performance).

Validation Bootstraps

30 bootstrap replicates of v68 Release will be utilized to validate the models. There are 257697 profiles utilized for this purpose.

[1] "Loading previously created bootstraps from file"

Oversampling

The entire v67 dataset will be utilized for training. However, most of the matching algorithms are more performant with higher ratios of Beta to Release. Therefore, various levels of oversampled datasets are created for training. There are 70345 profiles utilized for this purpose.

[1] "Loading previously created oversampling training sets from file"

Model Training

Two matching methods were ultimately tested, as CEM and subclassing were producing extremely poor results.

  • nearest-neighbors
  • genetic matching

For all models the following diagnostics are reported:

  • mean, median score against the 30 validation replicates
  • Number of matched Beta samples
  • QQ and ridge plots of original and matched Beta samples

Nearest-Neighbors, Malahanobis

Hyperparameters: hyperparameter_tuning_nn_malahanobis.Rmd

Feature selection and hyperparameter tuning for the nearest-neighbors models were all trained using the MatchIt library. These models are trained using the full, relevantly oversampled, training dataset.

FS 1

  • caliper = 0.20
  • calcloset = TRUE
  • ratio = 2
  • replace = FALSE
  • 4x oversampling

Resultant scores: 8.201553210^{-4}, 8.12712610^{-4}

  • Number of matched Beta training samples: 29814
  • Number of matched Beta validation samples: 20922

FS 3

  • caliper = 3.00
  • calcloset = TRUE
  • ratio = 1
  • replace = TRUE
  • 16x oversampling

Resultant scores: 0.0022122, 0.0022092

  • Number of matched Beta training samples: 3259
  • Number of matched Beta validation samples: 2199

FS 5

  • caliper = 0
  • calcloset = TRUE
  • ratio = 1
  • replace = FALSE
  • 8x oversampling

Resultant scores: 0.0015801, 0.0015768

  • Number of matched Beta training samples: 6208
  • Number of matched Beta validation samples: 4226

Nearest-Neighbors Logit, Linear

Hyperparameters: hyperparameter_tuning_nn_logit_linear.Rmd

FS 1

  • caliper = 0.4
  • calcloset = TRUE
  • ratio = 3
  • replace = FALSE
  • 4x oversampling
  • Interactions across covariates

Resultant scores: 7.762891510^{-4}, 7.683315510^{-4}

  • Number of matched Beta training samples: 44721
  • Number of matched Beta validation samples: 30644

FS 5

  • caliper = 0
  • calcloset = FALSE
  • ratio = 1
  • replace = FALSE
  • 8x oversampling

Resultant scores: 0.0013756, 0.001376

  • Number of matched Beta training samples: 7453
  • Number of matched Beta validation samples: 4825

Nearest-Neighbors GAM, Logit

Hyperparameters: hyperparameter_tuning_nn_gam_logit.Rmd

FS 1

  • caliper = 0.5
  • calcloset = FALSE
  • ratio = 3
  • replace = FALSE
  • 4x oversampling
  • Interactions across covariates

Resultant scores: 0.0011714, 0.0011762

  • Number of matched Beta training samples: 44721
  • Number of matched Beta validation samples: 30070

FS 5

  • caliper = 0
  • calcloset = FALSE
  • ratio = 1
  • replace = FALSE
  • 4x oversampling

Resultant scores: 0.002441, 0.0024395

  • Number of matched Beta training samples: 14907
  • Number of matched Beta validation samples: 9704

Nearest-Neighbors Probit, Linear

Hyperparameters: hyperparameter_tuning_nn_probit_linear.Rmd

FS 1

  • caliper = 0.4
  • calcloset = FALSE
  • ratio = 3
  • replace = FALSE
  • 4x oversampling
  • Interactions across covariates

Resultant scores: 7.430882310^{-4}, 7.367975710^{-4}

  • Number of matched Beta training samples: 44721
  • Number of matched Beta validation samples: 30607

FS 5

  • caliper = 0
  • calcloset = FALSE
  • ratio = 1
  • replace = FALSE
  • 8x oversampling

Resultant scores: 0.0013792, 0.001382

  • Number of matched Beta training samples: 7453
  • Number of matched Beta validation samples: 4958

Genetic Matching

Due to the very long training times of genetic matching, feature selection and hyperparameter tuning were not performed using bootstrap sampes. The models were previously trained using the full training sample. The resultant match beta subsets were serialized and uploaded to GCP.

Trained: feature_selection_genmatch_linear.Rmd

FS 1

Resultant scores: 0.001084, 0.001084

  • Number of matched Beta training samples: 25846
  • Number of matched Beta validation samples: 17684

Pop.Size 500

Resultant scores: 9.916087510^{-4}, 9.923251710^{-4}

  • Number of matched Beta training samples: 25930
  • Number of matched Beta validation samples: 17731

FS 3

Resultant scores: 0.0020662, 0.0020654

  • Number of matched Beta training samples: 22648
  • Number of matched Beta validation samples: 15929

FS 5

Resultant scores: 0.0020716, 0.0020748

  • Number of matched Beta training samples: 22469
  • Number of matched Beta validation samples: 15759

Results

Nearest-Neighbors Probit, Linear produces the optimal matching results. It yields the lowest scores, without dropping large sample of the Beta profiles. Unsurprisingly, matching directly, and only, on the performance metrics yields the best results. Interestingly, it appears that interactions across covariates greatly help. Fortunately, this model architecture is relatively cheap from a computational perspective.

Balancing on additional covariates (e.g, FS3 or FS5), outside of the performance metrics, yields sub-optimal results. Nearest-Neighbors Probit, Linear again yields the lowest score (0.0013792, 0.001382); however, this is at the expense of a greatly reduced Beta samples size (4958). Genetic matching is able to retain a much larger sample size (15929), at the expense of greater error (0.0020662, 0.0020654).

Conclusion: Optimal model framework

  • matching algorithm: Nearest-Neighbors Probit, Linear
  • features: Only performance metrics
  • hyperparameters:
    • caliper = 0.4
    • calcloset = FALSE
    • ratio = 3
    • replace = FALSE
    • 4x oversampling
    • Interactions across covariates

Push to GCP

The boostrap replicates for validation, oversampled traininig datasets, resultant quantile calculations, and matched datasets are saved to an R image. Then uploaded to the project GCP bucket.

Auto-refreshing stale OAuth token.
[1] "Previously trained results already exist: data/milestone2/validation_results_20191121.RData"
LS0tCnRpdGxlOiAnTWlsZXN0b25lIDI6IE1vZGVsIFZhbGlkYXRpb24nCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwpkYXRlOiAnTGFzdCBVcGRhdGVkOiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgIiVCICVkLCAlWSIpYCcKLS0tCgojIHRsO2RyIApUaGlzIG5vdGVib29rIGRldGVybWluZXMgdGhlIFttb2RlbCBmcmFtZXdvcmtdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL2RvY3VtZW50L2QvMVNmdWFudm1ZbXZtRUZBZFE3WjVkakRlTGV6ZE5CMVRFU3FWbWo5M084dG8vZWRpdCNoZWFkaW5nPWguZXgza2s3emQ1YTF5KSB1dGlsaXplZCBmb3IgdGhpcyBbUFJEXShodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9kb2N1bWVudC9kLzFZZ3o2TWt1ZFlIWmpuRG5EOVo5N2tVeUZydlYzS0dXc2pYeVBqZGRoSHEwL2VkaXQjaGVhZGluZz1oLmx2YjlsOGd3Mm5lZSkuIEl0IHV0aWxpemVzIHRoZSByZXN1bHRzIGZyb20gdGhlIGh5cGVycGFyYW1ldGVyIHR1bmluZyBzdGVwLCBhbmQgdHJhaW5zIHRoZSBvcHRpbWFsIG1vZGVsIGZvciBlYWNoIGFsZ29yaXRobSBvbiB0aGUgdjY3IGRhdGFzZXQuIFRoZSByZXN1bHRhbnQgcHJlZGljdGlvbnMgb2YgdGhlc2Ugb3B0aW1hbCBtb2RlbHMgYXJlIGNvbXBhcmVkIHRvIGEgdjY4IHZhbGlkYXRpb24gZGF0YXNldC4gVGhlIGZyYW1ld29yayB1c2VkIHRvIGdlbmVyYXRlIHRoZSBoaWdoZXN0IHBlcmZvcm1hbnQgbW9kZWwgKGUuZy4sIGZlYXR1cmVzLCBoeXBlcnBhcmFtZXRlcnMpIHdpbGwgYmUgdXNlZCBpbiBvbmxpbmUgbW9kZWwgdHJhaW5pbmcgZm9yIHRoZSBzdWJzZXR0aW5nIG9mIEJldGEgaW4gcHJvZHVjdGlvbiB1c2UuCgpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0Kc291cmNlKCcuLi9saWIvc3VwcG9ydGluZ19mdW5jcy5SJykKc291cmNlKCcuLi9saWIvc2NvcmluZy5SJykKbGlicmFyeShNYXRjaEl0KQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkodmlyaWRpcykKYGBgCgpgYGB7ciBkYXRhX2xvYWQsIGVjaG89RkFMU0V9CmZpbGVfbmFtZSA9ICdkZl90cmFpbl92YWxpZGF0ZV8yMDE5MTAyNS5SRGF0YScKaW1hZ2VfZmlsZV9wYXRoID0gZmlsZS5wYXRoKCdkYXRhJywgZmlsZV9uYW1lKQoKIyBQdWxsIGZyb20gR0NQIGlmIG5lY2Vzc2FyeQppZiAoIWZpbGUuZXhpc3RzKGltYWdlX2ZpbGVfcGF0aCkpewogIFN5cy5zZXRlbnYoIkdDU19ERUZBVUxUX0JVQ0tFVCIgPSAibW96LWZ4LWRldi1zdWJiZXRhIiwKICAgICAgICAgICAiR0NTX0FVVEhfRklMRSIgPSAibW96LWZ4LWRldi1jZG93aHlnbHVuZC1zdWJCZXRhLTc4OGY4ZjBkNDYyNy5qc29uIikKICBsaWJyYXJ5KGdvb2dsZUNsb3VkU3RvcmFnZVIpCiAgZ2NzX2dldF9vYmplY3QoZmlsZS5wYXRoKCdkYXRhJywgJ21pbGVzdG9uZTInLCBmaWxlX25hbWUpLCBzYXZlVG9EaXNrID0gaW1hZ2VfZmlsZV9wYXRoLCBvdmVyd3JpdGUgPSBUUlVFKQp9Cgpsb2FkKGltYWdlX2ZpbGVfcGF0aCkKYGBgCgpgYGB7ciB2YXJfZGVmLCBlY2hvPUZBTFNFfQpkZl9yZWxfdmFsIDwtIGRmX3ZhbGlkYXRlX2YgJT4lCiAgZmlsdGVyKGxhYmVsID09ICdyZWxlYXNlJykKCmRmX2JldGFfdHJhaW4gPC0gZGZfdHJhaW5fZiAlPiUgZmlsdGVyKGlzX3JlbGVhc2UgPT0gRkFMU0UpCmRmX2JldGFfdmFsIDwtIGRmX3ZhbGlkYXRlX2YgJT4lIGZpbHRlcihpc19yZWxlYXNlID09IEZBTFNFKQpkZl9yZWxfdHJhaW4gPC0gZGZfdHJhaW5fZiAlPiUgZmlsdGVyKGlzX3JlbGVhc2UgPT0gVFJVRSkKbl9iZXRhIDwtIG5yb3coZGZfYmV0YV92YWwpCmBgYAoKIyBGZWF0dXJlcwpGZWF0dXJlIHNldHM6IAoKMS4gT25seSBwZXJmb3JtYW5jZSBtZXRyaWNzLgoyLiBQZXJmb3JtYW5jZSBtZXRyaWNzIGFuZCB0aGUgaGlnaGVzdCBmb3VuZCBieSBCb3J1dGEgd2l0aCBvcmlnaW5hbCBkYXRhc2V0LgozLiBQZXJmb3JtYW5jZSBtZXRyaWNzIGFuZCB0aGUgaGlnaGVzdCBmb3VuZCBieSBCb3J1dGEgd2l0aCBlcXVhbCBsYWJlbHMgZGF0YXNldC4KNC4gUGVyZm9ybWFuY2UgbWV0cmljcyBhbmQgYWxsIGNvdmFyaWF0ZXMuCjUuIE9ubHkgdGhlIGhpZ2hlc3QgY292YXJpYXRlcyBmb3VuZCBieSBCb3J1dGEgKGV4Y2x1ZGluZyBwZXJmb3JtYW5jZSkuCjYuIE9ubHkgdGhlIGhpZ2hlc3QgY292YXJpYXRlcyBmb3VuZCBieSBCb3J1dGEgKGVxdWFsIGxhYmVscywgZXhjbHVkaW5nIHBlcmZvcm1hbmNlKS4KNy4gVXRpbGl6ZSBhbGwgY292YXJpYXRlcyAoZXhjbHVkaW5nIHBlcmZvcm1hbmNlKS4gCgoKYGBge3IgYm9ydXRhX2ltcG9ydCwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZmlsZV9uYW1lID0gJ2ZlYXR1cmVfc2VsZWN0aW9uX2JvcnV0YV9pbml0aWFsXzIwMTkxMDIzLlJEYXRhJwppbWFnZV9maWxlX3BhdGggPSBmaWxlLnBhdGgoJ2RhdGEnLCBmaWxlX25hbWUpCgppZiAoIWZpbGUuZXhpc3RzKGltYWdlX2ZpbGVfcGF0aCkpewogIFN5cy5zZXRlbnYoIkdDU19ERUZBVUxUX0JVQ0tFVCIgPSAibW96LWZ4LWRldi1zdWJiZXRhIiwKICAgICAgICAgICAiR0NTX0FVVEhfRklMRSIgPSAibW96LWZ4LWRldi1jZG93aHlnbHVuZC1zdWJCZXRhLTc4OGY4ZjBkNDYyNy5qc29uIikKICBsaWJyYXJ5KGdvb2dsZUNsb3VkU3RvcmFnZVIpCiAgZ2NzX2dldF9vYmplY3QoZmlsZS5wYXRoKCdkYXRhJywgJ21pbGVzdG9uZTInLCBmaWxlX25hbWUpLCBzYXZlVG9EaXNrID0gaW1hZ2VfZmlsZV9wYXRoLCBvdmVyd3JpdGUgPSBUUlVFKQp9CmxvYWQoaW1hZ2VfZmlsZV9wYXRoKQpgYGAKCmBgYHtyIGJvcnV0YV9mcywgZWNobz1GQUxTRX0KZXh0cmFjdF9ib3J1dGFfZnMgPC0gZnVuY3Rpb24oYm9ydXRhX3JlcywgbnVtPTUpewogIGZlYXR1cmVzIDwtIE5VTEwKICBmb3IobWV0cmljIGluIG5hbWVzKGJvcnV0YV9yZXN1bHRzKSl7CiAgICBmZWF0dXJlcyA8LSBjKG5hbWVzKHNvcnQoYXBwbHkoYm9ydXRhX3Jlc1tbbWV0cmljXV0kSW1wSGlzdG9yeSwgMiwgbWVkaWFuKSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6bnVtXSksIGZlYXR1cmVzKQogIH0KICByZXR1cm4oc29ydCh1bmlxdWUoZmVhdHVyZXMpKSkKfQoKZmVhdHVyZXNfdG9wMTAgPC0gZXh0cmFjdF9ib3J1dGFfZnMoYm9ydXRhX3Jlc3VsdHMsIG51bT0xMCkKZmVhdHVyZXNfdG9wMTBfZXEgPC0gZXh0cmFjdF9ib3J1dGFfZnMoYm9ydXRhX3Jlc3VsdHNfZXEsIG51bT0xMCkKCiMgZmlsdGVyIG91dCBjYXRlZ29yaWNhbApmZWF0dXJlc190b3AxMCA8LSBkZl90cmFpbl9mICU+JSAKICBzZWxlY3QoZmVhdHVyZXNfdG9wMTApICU+JSAKICBzZWxlY3RfaWYoaXMubnVtZXJpYykgJT4lIAogIG5hbWVzKCkKZmVhdHVyZXNfdG9wMTBfZXEgPC0gZGZfdHJhaW5fZiAlPiUgCiAgc2VsZWN0KGZlYXR1cmVzX3RvcDEwX2VxKSAlPiUgCiAgc2VsZWN0X2lmKGlzLm51bWVyaWMpICU+JSAKICBuYW1lcygpCmBgYAoKYGBge3IgZmVhdHVyZV9zZXRzLCBlY2hvPUZBTFNFfQpwZXJmX21ldHJpY3MgPC0gbmFtZXMoZ2V0X20yX21ldHJpY19tYXAoKSkKCmNvdnMgPC0gZGZfdHJhaW5fZiAlPiUKICBzZWxlY3QoLXBlcmZfbWV0cmljcykgJT4lCiAgc2VsZWN0KC1jb250ZW50X2NyYXNoZXMpICU+JQogIHNlbGVjdCgtY2xpZW50X2lkKSAlPiUKICBzZWxlY3QoLWxhYmVsKSAlPiUKICBzZWxlY3QoLWlzX3JlbGVhc2UpICU+JQogIHNlbGVjdCgtYXBwX3ZlcnNpb24pICU+JQogIHNlbGVjdF9pZihpcy5udW1lcmljKSAlPiUgIyBNYWhhbGFub2JpcyBjb25zdHJhaW50CiAgbmFtZXMoKQoKZnMxIDwtIHBlcmZfbWV0cmljcwpmczIgPC0gYyhuYW1lcyhmczEpLCBmZWF0dXJlc190b3AxMCkKZnMzIDwtIGMobmFtZXMoZnMxKSwgZmVhdHVyZXNfdG9wMTBfZXEpCmZzNCA8LSBjKG5hbWVzKGZzMSksIGNvdnMpCmZzNSA8LSBmZWF0dXJlc190b3AxMApmczYgPC0gZmVhdHVyZXNfdG9wMTBfZXEKZnM3IDwtIGNvdnMKYGBgCgojIFZhbGlkYXRpb24gQm9vdHN0cmFwcwoKMzAgYm9vdHN0cmFwIHJlcGxpY2F0ZXMgb2YgdjY4IFJlbGVhc2Ugd2lsbCBiZSB1dGlsaXplZCB0byB2YWxpZGF0ZSB0aGUgbW9kZWxzLiBUaGVyZSBhcmUgYHIgbnJvdyhkZl9yZWxfdmFsKWAgcHJvZmlsZXMgdXRpbGl6ZWQgZm9yIHRoaXMgcHVycG9zZS4gCgpgYGB7ciBidHNfdmFsaWRhdGUsIGVjaG89RkFMU0V9CiMgY3JlYXRlIG9uY2UKZmlsZV9uYW1lID0gJ3ZhbGlkYXRpb25fYnRzXzIwMTkxMTA2LlJEYXRhJwpidHNfZmlsZV9wYXRoID0gZmlsZS5wYXRoKCdkYXRhJywgZmlsZV9uYW1lKQoKaWYgKCFmaWxlLmV4aXN0cyhidHNfZmlsZV9wYXRoKSl7CiAgcHJpbnQoJ0NyZWF0aW5nIHZhbGlkYXRpb24gYm9vdHN0cmFwcyBhbmQgc2F2aW5nJykKICBidHMgPSBsaXN0KCkKICBmb3IoaSBpbiAxOjMwKXsKICAgIGJ0c1tbaV1dIDwtIGRmX3JlbF92YWwgJT4lIAogICAgICBzYW1wbGVfZnJhYyhzaXplID0gMSwgcmVwbGFjZSA9IFRSVUUpICU+JQogICAgICBwdWxsKGNsaWVudF9pZCkgCiAgfQogIHNhdmUoYnRzLCBmaWxlID0gYnRzX2ZpbGVfcGF0aCkKfSBlbHNlIHsKICBwcmludCgnTG9hZGluZyBwcmV2aW91c2x5IGNyZWF0ZWQgYm9vdHN0cmFwcyBmcm9tIGZpbGUnKQogIGxvYWQoYnRzX2ZpbGVfcGF0aCkKfQpgYGAKCmBgYHtyIHNjb3JlciwgZWNobz1GQUxTRX0Kc2NvcmVfbW9kZWwgPC0gZnVuY3Rpb24oYnRzLCBkZl9tYXRjaCwgZGZfdmFsLCB3b3JrZXJzKXsKICBpZiAobWlzc2luZyh3b3JrZXJzKSkgd29ya2VycyA9IGRldGVjdENvcmVzKCkKICBjbCA8LSBtYWtlUFNPQ0tjbHVzdGVyKHdvcmtlcnMpIAogIHJlZ2lzdGVyRG9QYXJhbGxlbChjbCkKICBmaW5hbCA8LSB0cnlDYXRjaCh7CiAgICBzY29yZXMgPC0gZm9yZWFjaChpPTE6bGVuZ3RoKGJ0cyksIAogICAgICAgICAgICAgICAgICAgICAgLnBhY2thZ2VzID0gYygnZHBseXInLCAndHJhbnNwb3J0JyksIAogICAgICAgICAgICAgICAgICAgICAgLmV4cG9ydD1jKCdjYWxjX3Njb3JlJywgJ2NhbGNfY21zJywgJ2dldF9tMl9tZXRyaWNfbWFwJykpICVkb3BhciUgewogICAgICAgICAgICAgICAgICAgICAgICBidCA8LSBidHNbW2ldXQogICAgICAgICAgICAgICAgICAgICAgICB0ZXN0IDwtIGRmX3ZhbCAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmlnaHRfam9pbihkYXRhLmZyYW1lKGNsaWVudF9pZCA9IGJ0LCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKSwgYnk9J2NsaWVudF9pZCcsICdyaWdodCcpCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICBkZl9zY29yZXMgPC0gdGVzdCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICBiaW5kX3Jvd3MoZGZfbWF0Y2gpCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICBzY29yZSA8LSBjYWxjX3Njb3JlKGRmX3Njb3JlcywgZ2V0X20yX21ldHJpY19tYXAoKSkKICAgICAgICAgICAgICAgICAgICAgICAgc2NvcmUKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgc2NvcmVzIDwtIHVubGlzdChzY29yZXMpCiAgICBjKG1lYW4gPSBtZWFuKHNjb3JlcyksIG1lZGlhbiA9IG1lZGlhbihzY29yZXMpKQogIH0sIAogIGVycm9yID0gZnVuY3Rpb24oY29uZCl7CiAgICBtZXNzYWdlKHBhc3RlKCJCb290c3RyYXAgdmFsaWRhdGlvbiBmYWlsZWQ6ICIsIGNvbmQpKQogICAgcmV0dXJuKE5BKQogIH0sCiAgZmluYWxseSA9IHsKICAgIHN0b3BDbHVzdGVyKGNsKQogIH0KICApCiAgcmV0dXJuKGZpbmFsKQp9CgpidWlsZF9xdWFudGlsZV9kZiA8LSBmdW5jdGlvbih2YWxpZGF0aW9uLCBtYXRjaGVkLCBvcmlnaW5hbCl7CiAgcXFzIDwtIGxpc3QoKQogIGZvciAocGVyZl9tZXRyaWMgaW4gcGVyZl9tZXRyaWNzKXsKICAgIHFxIDwtIHFxcGxvdCh2YWxpZGF0aW9uW1twZXJmX21ldHJpY11dLCBtYXRjaGVkW1twZXJmX21ldHJpY11dLCBwbG90Lml0ID0gRkFMU0UpICU+JSAKICAgICAgYmluZF9yb3dzKCkgJT4lCiAgICAgIG11dGF0ZSh0eXBlID0gJ21hdGNoZWQnKQogICAgcXFfZnVsbCA8LSBxcXBsb3QodmFsaWRhdGlvbltbcGVyZl9tZXRyaWNdXSwgb3JpZ2luYWxbW3BlcmZfbWV0cmljXV0sIHBsb3QuaXQgPSBGQUxTRSkgJT4lIAogICAgICBiaW5kX3Jvd3MoKSAlPiUKICAgICAgbXV0YXRlKHR5cGUgPSAnb3JpZ2luYWwnKSAlPiUKICAgICAgYmluZF9yb3dzKHFxKSAlPiUKICAgICAgbXV0YXRlKG1ldHJpYyA9IHBlcmZfbWV0cmljKQogICAgIyBxcV9mdWxsJG1ldHJpYyA8LSBwZXJmX21ldHJpYwogICAgcXFzW1twZXJmX21ldHJpY11dIDwtIHFxX2Z1bGwKICB9CiAgcXFfZGYgPC0gcXFzICU+JSBiaW5kX3Jvd3MoKSAlPiUgcmVuYW1lKHJlbGVhc2UgPSB4LCBiZXRhID0geSkKICByZXR1cm4ocXFfZGYpCn0KCnBsb3RfdmFsaWRhdGlvbl9yZXN1bHRzIDwtIGZ1bmN0aW9uKHZhbGlkYXRpb24sIG1hdGNoZWQsIG9yaWdpbmFsLCBxcV9kZil7CiAgaWYgKG1pc3NpbmcocXFfZGYpKSBxcV9kZiA8LSBidWlsZF9xdWFudGlsZV9kZih2YWxpZGF0aW9uLCBtYXRjaGVkLCBvcmlnaW5hbCkKICAKICBkZiA8LSBtYXRjaGVkICU+JQogICAgICBtdXRhdGUobGFiZWwgPSAnYmV0YSAtIG1hdGNoZWQnKSAlPiUKICAgICAgYmluZF9yb3dzKHZhbGlkYXRpb24pICU+JQogICAgICBiaW5kX3Jvd3Mob3JpZ2luYWwpICU+JQogICAgICBzZWxlY3QocGVyZl9tZXRyaWNzLCBsYWJlbCkgIyAlPiUKICAgICAgIyBnYXRoZXIoa2V5ID0gJ21ldHJpYycsIHZhbHVlID0gJ21lYXN1cmVtZW50JywgLWxhYmVsKQogIAogIHBsb3RzIDwtIGxpc3QoKQogIGZvciAocG1ldCBpbiBwZXJmX21ldHJpY3MpewogICAgcF9xcSA8LSBnZ3Bsb3QocXFfZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gcG1ldCksIGFlcyh4ID0gcmVsZWFzZSwgeSA9IGJldGEpKSArCiAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gdHlwZSwgc2hhcGUgPSB0eXBlKSkgKyAKICAgICAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwKSArIAogICAgICB0aGVtZV9idygpICsgCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LCAwLjIpKSArIAogICAgICBnZ3RpdGxlKHBtZXQpIAogICAgCiAgICBwX3JpZGdlIDwtIGdncGxvdChkZiwgYWVzKHg9ISFzeW0ocG1ldCksIHk9bGFiZWwsIGZpbGw9ZmFjdG9yKC4ucXVhbnRpbGUuLikpKSArCiAgICBzdGF0X2RlbnNpdHlfcmlkZ2VzKAogICAgICBnZW9tID0gImRlbnNpdHlfcmlkZ2VzX2dyYWRpZW50IiwgY2FsY19lY2RmID0gVFJVRSwKICAgICAgcXVhbnRpbGVzID0gMTAsIHF1YW50aWxlX2xpbmVzID0gVFJVRQogICAgKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFLCBuYW1lID0gIlF1YXJ0aWxlcyIpICsgCiAgICAgIHRoZW1lX2J3KCkgKwogICAgICB4bGFiKHBtZXQpICsgCiAgICAgIHhsaW0oYygwLCAxMDAwMCkpICsgCiAgICAgIGd1aWRlcyhmaWxsID0gRkFMU0UpCiAgICAKICAgIHBsb3RzW1twYXN0ZShwbWV0LCAncXEnLCBzZXA9Il8iKV1dIDwtIHBfcXEKICAgIHBsb3RzW1twYXN0ZShwbWV0LCAncmlkZ2UnLCBzZXA9Il8iKV1dIDwtIHBfcmlkZ2UKICB9CiAgCiAgcHJpbnQocGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdHMsIG5jb2wgPSAyKSkKfQpgYGAKCiMgT3ZlcnNhbXBsaW5nCgpUaGUgZW50aXJlIHY2NyBkYXRhc2V0IHdpbGwgYmUgdXRpbGl6ZWQgZm9yIHRyYWluaW5nLiBIb3dldmVyLCBtb3N0IG9mIHRoZSBtYXRjaGluZyBhbGdvcml0aG1zIGFyZSBtb3JlIHBlcmZvcm1hbnQgd2l0aCBoaWdoZXIgcmF0aW9zIG9mIEJldGEgdG8gUmVsZWFzZS4gVGhlcmVmb3JlLCB2YXJpb3VzIGxldmVscyBvZiBvdmVyc2FtcGxlZCBkYXRhc2V0cyBhcmUgY3JlYXRlZCBmb3IgdHJhaW5pbmcuIFRoZXJlIGFyZSBgciBuX2JldGFgIHByb2ZpbGVzIHV0aWxpemVkIGZvciB0aGlzIHB1cnBvc2UuCgpgYGB7ciBvdmVyc2FtcGxpbmcsIGVjaG89RkFMU0V9CiMgY3JlYXRlIG9uY2UKCmZpbGVfbmFtZSA9ICd0cmFpbmluZ19maW5hbF9vdmVyc2FtcGxlc18yMDE5MTEwNi5SRGF0YScKb3ZlcnNhbXBsZXNfZmlsZV9wYXRoID0gZmlsZS5wYXRoKCdkYXRhJywgZmlsZV9uYW1lKQoKaWYgKCFmaWxlLmV4aXN0cyhvdmVyc2FtcGxlc19maWxlX3BhdGgpKXsKICBwcmludCgnQ3JlYXRpbmcgb3ZlcnNhbXBsaW5nIHRyYWluaW5nIHNldHMgYW5kIHNhdmluZycpCiAgb3ZlcnNhbXBsZSA8LSBmdW5jdGlvbihvdmVyc2FtcGxpbmcsIGRmX2JldGEsIGRmX3JlbCkgewogICAgZGZfeCA8LSBkZl9yZWwgJT4lCiAgICAgIHNhbXBsZV9uKHNpemUgPSByb3VuZChuX2JldGEgLyBvdmVyc2FtcGxpbmcpKSAlPiUKICAgICAgcmJpbmQoZGZfYmV0YSkKICAgIHJldHVybihkZl94KQogICAgfQogIAogIG92ZXJzYW1wbGVzIDwtIGMoMSwgMiwgNCwgOCwgMTYpCiAgZGZzIDwtIGxhcHBseShvdmVyc2FtcGxlcywgb3ZlcnNhbXBsZSwgZGZfYmV0YSA9IGRmX2JldGFfdHJhaW4sIGRmX3JlbCA9IGRmX3JlbF90cmFpbikKICBuYW1lcyhkZnMpIDwtIGFzLmNoYXJhY3RlcihvdmVyc2FtcGxlcykKICBzYXZlKGRmcywgZmlsZSA9IG92ZXJzYW1wbGVzX2ZpbGVfcGF0aCkKfSBlbHNlIHsKICBwcmludCgnTG9hZGluZyBwcmV2aW91c2x5IGNyZWF0ZWQgb3ZlcnNhbXBsaW5nIHRyYWluaW5nIHNldHMgZnJvbSBmaWxlJykKICBsb2FkKG92ZXJzYW1wbGVzX2ZpbGVfcGF0aCkKfQpgYGAKCiMgTW9kZWwgVHJhaW5pbmcKClR3byBtYXRjaGluZyBtZXRob2RzIHdlcmUgdWx0aW1hdGVseSB0ZXN0ZWQsIGFzIENFTSBhbmQgc3ViY2xhc3Npbmcgd2VyZSBwcm9kdWNpbmcgZXh0cmVtZWx5IHBvb3IgcmVzdWx0cy4gCgoqIG5lYXJlc3QtbmVpZ2hib3JzCiogZ2VuZXRpYyBtYXRjaGluZwoKRm9yIGFsbCBtb2RlbHMgdGhlIGZvbGxvd2luZyBkaWFnbm9zdGljcyBhcmUgcmVwb3J0ZWQ6CgoqIG1lYW4sIG1lZGlhbiBzY29yZSBhZ2FpbnN0IHRoZSAzMCB2YWxpZGF0aW9uIHJlcGxpY2F0ZXMKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHNhbXBsZXMKKiBRUSBhbmQgcmlkZ2UgcGxvdHMgb2Ygb3JpZ2luYWwgYW5kIG1hdGNoZWQgQmV0YSBzYW1wbGVzCgojIyBOZWFyZXN0LU5laWdoYm9ycywgTWFsYWhhbm9iaXMgCgpIeXBlcnBhcmFtZXRlcnM6IGBoeXBlcnBhcmFtZXRlcl90dW5pbmdfbm5fbWFsYWhhbm9iaXMuUm1kYAoKRmVhdHVyZSBzZWxlY3Rpb24gYW5kIGh5cGVycGFyYW1ldGVyIHR1bmluZyBmb3IgdGhlIG5lYXJlc3QtbmVpZ2hib3JzIG1vZGVscyB3ZXJlIGFsbCB0cmFpbmVkIHVzaW5nIHRoZSBgTWF0Y2hJdGAgbGlicmFyeS4gIFRoZXNlIG1vZGVscyBhcmUgdHJhaW5lZCB1c2luZyB0aGUgZnVsbCwgcmVsZXZhbnRseSBvdmVyc2FtcGxlZCwgdHJhaW5pbmcgZGF0YXNldC4gIAoKYGBge3IgdHJhaW5lciwgZWNobz1GQUxTRX0KdHJhaW5fbWF0Y2hpdCA8LSBmdW5jdGlvbih0cmFpbiwgbW9kZWxfY292cywgYWRkX2ludGVyYWN0aW9ucywgLi4uKXsKICAjIHRyYWluIG1vZGVsCiAgZm9ybXVsYSA8LSBnZW5lcmF0ZV9mb3JtdWxhKG1vZGVsX2NvdnMsIGxhYmVsID0gJ2lzX3JlbGVhc2UnLCBhZGRfaW50ZXJhY3Rpb25zKQogIG1vZGVsIDwtIG1hdGNoaXQoZm9ybXVsYSwgdHJhaW4sIC4uLikKICAKICAjIGV4dHJhY3QgYmV0YSBzdWJzZXQKICBkZl9tYXRjaGVkIDwtIGdldF9tYXRjaGVzKG1vZGVsLCB0cmFpbikgJT4lCiAgICBzZWxlY3QoLXdlaWdodHMsIC1kaXN0YW5jZSkgJT4lCiAgICBmaWx0ZXIobGFiZWwgPT0gJ2JldGEnKQogIAogIHJldHVybihsaXN0KG1vZGVsID0gbW9kZWwsIG1hdGNoZWQgPSBkZl9tYXRjaGVkKSkKfQoKZXh0cmFjdF9wcmVkaWN0aW9ucyA8LSBmdW5jdGlvbihtYXRjaGVkLCB2YWxpZGF0aW9uKXsKICBwcmVkaWN0aW9uIDwtIHZhbGlkYXRpb24gJT4lCiAgICBmaWx0ZXIoY2xpZW50X2lkICVpbiUgbWF0Y2hlZCRjbGllbnRfaWQpCiAgCiAgcmV0dXJuKHByZWRpY3Rpb24pCn0KYGBgCgojIyMgRlMgMQoKKiBgY2FsaXBlcmAgPSAwLjIwCiogYGNhbGNsb3NldGAgPSBUUlVFCiogYHJhdGlvYCA9IDIKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDR4IG92ZXJzYW1wbGluZwoKYGBge3Igbm5fbWFsX2ZzMSwgZWNobz1GQUxTRX0KIyBubi5tYWwuZnMxIDwtIHRyYWluX21hdGNoaXQoZGZzW1snNCddXSwgZnMxLCBhZGRfaW50ZXJhY3Rpb25zID0gRkFMU0UsIHJlcGxhY2UgPSBGQUxTRSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgY2FsaXBlciA9IDAuMjUsIGNhbGNsb3Nlc3QgPSBUUlVFLCByYXRpbyA9IDIsIGRpc3RhbmNlID0gIm1haGFsYW5vYmlzIikKbm4ubWFsLmZzMS5wcmVkaWN0aW9ucyA8LSBleHRyYWN0X3ByZWRpY3Rpb25zKG5uLm1hbC5mczEkbWF0Y2hlZCwgZGZfYmV0YV92YWwpCm5uLm1hbC5mczEucXEgPC0gYnVpbGRfcXVhbnRpbGVfZGYoZGZfcmVsX3ZhbCwgbm4ubWFsLmZzMS5wcmVkaWN0aW9ucywgZGZfYmV0YV92YWwpCm5uLm1hbC5mczEuc2NvcmUgPC0gc2NvcmVfbW9kZWwoYnRzLCBubi5tYWwuZnMxLnByZWRpY3Rpb25zLCBkZl9yZWxfdmFsKQojIG5uLm1hbC5mczEuc2NvcmUKYGBgCgpSZXN1bHRhbnQgc2NvcmVzOiBgciBubi5tYWwuZnMxLnNjb3JlYAoKCiogTnVtYmVyIG9mIG1hdGNoZWQgQmV0YSB0cmFpbmluZyBzYW1wbGVzOiBgciBucm93KG5uLm1hbC5mczEkbWF0Y2hlZClgCiogTnVtYmVyIG9mIG1hdGNoZWQgQmV0YSB2YWxpZGF0aW9uIHNhbXBsZXM6IGByIG5yb3cobm4ubWFsLmZzMS5wcmVkaWN0aW9ucylgCgpgYGB7ciBubl9tYWxfZnMxX3ZhbF9wbHQsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTI1LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQpwbG90X3ZhbGlkYXRpb25fcmVzdWx0cyhkZl9yZWxfdmFsLCBubi5tYWwuZnMxLnByZWRpY3Rpb25zLCBkZl9iZXRhX3ZhbCwgbm4ubWFsLmZzMS5xcSkKYGBgCgojIyMgRlMgMyAKCiogYGNhbGlwZXJgID0gMy4wMAoqIGBjYWxjbG9zZXRgID0gVFJVRQoqIGByYXRpb2AgPSAxCiogYHJlcGxhY2VgID0gVFJVRQoqIDE2eCBvdmVyc2FtcGxpbmcKCmBgYHtyIG5uX21hbF9mczMsIGVjaG89RkFMU0V9CiMgbm4ubWFsLmZzMyA8LSB0cmFpbl9tYXRjaGl0KGRmc1tbJzE2J11dLCBmczMsIGFkZF9pbnRlcmFjdGlvbnMgPSBGQUxTRSwgcmVwbGFjZSA9IFRSVUUsIAojICAgICAgICAgICAgICAgICAgICAgICAgIGNhbGlwZXIgPSAzLjAwLCBjYWxjbG9zZXN0ID0gVFJVRSwgcmF0aW8gPSAxLCBkaXN0YW5jZSA9ICJtYWhhbGFub2JpcyIpCm5uLm1hbC5mczMucHJlZGljdGlvbnMgPC0gZXh0cmFjdF9wcmVkaWN0aW9ucyhubi5tYWwuZnMzJG1hdGNoZWQsIGRmX2JldGFfdmFsKQpubi5tYWwuZnMzLnFxIDwtIGJ1aWxkX3F1YW50aWxlX2RmKGRmX3JlbF92YWwsIG5uLm1hbC5mczMucHJlZGljdGlvbnMsIGRmX2JldGFfdmFsKQpubi5tYWwuZnMzLnNjb3JlIDwtIHNjb3JlX21vZGVsKGJ0cywgbm4ubWFsLmZzMy5wcmVkaWN0aW9ucywgZGZfcmVsX3ZhbCkKIyBubi5tYWwuZnMzLnNjb3JlCmBgYAoKUmVzdWx0YW50IHNjb3JlczogYHIgbm4ubWFsLmZzMy5zY29yZWAKCgoqIE51bWJlciBvZiBtYXRjaGVkIEJldGEgdHJhaW5pbmcgc2FtcGxlczogYHIgbnJvdyhubi5tYWwuZnMzJG1hdGNoZWQpYAoqIE51bWJlciBvZiBtYXRjaGVkIEJldGEgdmFsaWRhdGlvbiBzYW1wbGVzOiBgciBucm93KG5uLm1hbC5mczMucHJlZGljdGlvbnMpYAoKYGBge3Igbm5fbWFsX2ZzM192YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgbm4ubWFsLmZzMy5wcmVkaWN0aW9ucywgZGZfYmV0YV92YWwpCmBgYAoKCgojIyMgRlMgNQoKKiBgY2FsaXBlcmAgPSAwCiogYGNhbGNsb3NldGAgPSBUUlVFCiogYHJhdGlvYCA9IDEKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDh4IG92ZXJzYW1wbGluZwoKYGBge3Igbm5fbWFsX2ZzNSwgZWNobz1GQUxTRX0KIyBubi5tYWwuZnM1IDwtIHRyYWluX21hdGNoaXQoZGZzW1snOCddXSwgZnM1LCBhZGRfaW50ZXJhY3Rpb25zID0gRkFMU0UsIHJlcGxhY2UgPSBUUlVFLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICBjYWxpcGVyID0gMCwgY2FsY2xvc2VzdCA9IFRSVUUsIHJhdGlvID0gMSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdGFuY2UgPSAibWFoYWxhbm9iaXMiKQpubi5tYWwuZnM1LnByZWRpY3Rpb25zIDwtIGV4dHJhY3RfcHJlZGljdGlvbnMobm4ubWFsLmZzNSRtYXRjaGVkLCBkZl9iZXRhX3ZhbCkKbm4ubWFsLmZzNS5xcSA8LSBidWlsZF9xdWFudGlsZV9kZihkZl9yZWxfdmFsLCBubi5tYWwuZnM1LnByZWRpY3Rpb25zLCBkZl9iZXRhX3ZhbCkKbm4ubWFsLmZzNS5zY29yZSA8LSBzY29yZV9tb2RlbChidHMsIG5uLm1hbC5mczUucHJlZGljdGlvbnMsIGRmX3JlbF92YWwpCiMgbm4ubWFsLmZzNS5zY29yZQpgYGAKClJlc3VsdGFudCBzY29yZXM6IGByIG5uLm1hbC5mczUuc2NvcmVgCgoKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHRyYWluaW5nIHNhbXBsZXM6IGByIG5yb3cobm4ubWFsLmZzNSRtYXRjaGVkKWAKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHZhbGlkYXRpb24gc2FtcGxlczogYHIgbnJvdyhubi5tYWwuZnM1LnByZWRpY3Rpb25zKWAKCmBgYHtyIG5uX21hbF9mczVfdmFsX3BsdCwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9MjUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CnBsb3RfdmFsaWRhdGlvbl9yZXN1bHRzKGRmX3JlbF92YWwsIG5uLm1hbC5mczUucHJlZGljdGlvbnMsIGRmX2JldGFfdmFsKQpgYGAKCiMjIE5lYXJlc3QtTmVpZ2hib3JzIExvZ2l0LCBMaW5lYXIKCkh5cGVycGFyYW1ldGVyczogYGh5cGVycGFyYW1ldGVyX3R1bmluZ19ubl9sb2dpdF9saW5lYXIuUm1kYAoKYGBge3IgZmVhdHVyZV9zZXRzX25uLCBlY2hvPUZBTFNFfQojIFJlbG9hZCB0byBhZGQgYWRkaXRpb25hbCBjYXRlZ29yaWNhbCBjb3ZhcmlhdGVzCgpwZXJmX21ldHJpY3MgPC0gbmFtZXMoZ2V0X20yX21ldHJpY19tYXAoKSkKCmNvdnMgPC0gZGZfdHJhaW5fZiAlPiUKICBzZWxlY3QoLXBlcmZfbWV0cmljcykgJT4lCiAgc2VsZWN0KC1jb250ZW50X2NyYXNoZXMpICU+JQogIHNlbGVjdCgtY2xpZW50X2lkKSAlPiUKICBzZWxlY3QoLWxhYmVsKSAlPiUKICBzZWxlY3QoLWlzX3JlbGVhc2UpICU+JQogIHNlbGVjdCgtYXBwX3ZlcnNpb24pICU+JQogIG5hbWVzKCkKCmZzMSA8LSBwZXJmX21ldHJpY3MKZnMyIDwtIGMobmFtZXMoZnMxKSwgZmVhdHVyZXNfdG9wMTApCmZzMyA8LSBjKG5hbWVzKGZzMSksIGZlYXR1cmVzX3RvcDEwX2VxKQpmczQgPC0gYyhuYW1lcyhmczEpLCBjb3ZzKQpmczUgPC0gZmVhdHVyZXNfdG9wMTAKZnM2IDwtIGZlYXR1cmVzX3RvcDEwX2VxCmZzNyA8LSBjb3ZzCmBgYAoKIyMjIEZTIDEKCiogYGNhbGlwZXJgID0gMC40CiogYGNhbGNsb3NldGAgPSBUUlVFCiogYHJhdGlvYCA9IDMKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDR4IG92ZXJzYW1wbGluZwoqIEludGVyYWN0aW9ucyBhY3Jvc3MgY292YXJpYXRlcwoKYGBge3Igbm5fbG9naXRfZnMxLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgZWNobz1GQUxTRX0KIyBubi5saW5lYXIubG9naXQuZnMxIDwtIHRyYWluX21hdGNoaXQoZGZzW1snNCddXSwgZnMxLCBhZGRfaW50ZXJhY3Rpb25zID0gVFJVRSwgcmVwbGFjZSA9IEZBTFNFLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICBjYWxpcGVyID0gMC40LCBjYWxjbG9zZXN0ID0gVFJVRSwgcmF0aW8gPSAzLCBkaXN0YW5jZSA9ICJsaW5lYXIubG9naXQiKQpubi5saW5lYXIubG9naXQuZnMxLnByZWRpY3Rpb25zIDwtIGV4dHJhY3RfcHJlZGljdGlvbnMobm4ubGluZWFyLmxvZ2l0LmZzMSRtYXRjaGVkLCBkZl9iZXRhX3ZhbCkKbm4ubGluZWFyLmxvZ2l0LmZzMS5xcSA8LSBidWlsZF9xdWFudGlsZV9kZihkZl9yZWxfdmFsLCBubi5saW5lYXIubG9naXQuZnMxLnByZWRpY3Rpb25zLCBkZl9iZXRhX3ZhbCkKbm4ubGluZWFyLmxvZ2l0LmZzMS5zY29yZSA8LSBzY29yZV9tb2RlbChidHMsIG5uLmxpbmVhci5sb2dpdC5mczEucHJlZGljdGlvbnMsIGRmX3JlbF92YWwpCiMgbm4ubGluZWFyLmxvZ2l0LmZzMS5zY29yZQpgYGAKClJlc3VsdGFudCBzY29yZXM6IGByIG5uLmxpbmVhci5sb2dpdC5mczEuc2NvcmVgCgoKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHRyYWluaW5nIHNhbXBsZXM6IGByIG5yb3cobm4ubGluZWFyLmxvZ2l0LmZzMSRtYXRjaGVkKWAKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHZhbGlkYXRpb24gc2FtcGxlczogYHIgbnJvdyhubi5saW5lYXIubG9naXQuZnMxLnByZWRpY3Rpb25zKWAKCmBgYHtyIG5uX2xvZ2l0X2ZzMV92YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgbm4ubGluZWFyLmxvZ2l0LmZzMS5wcmVkaWN0aW9ucywgZGZfYmV0YV92YWwpCmBgYAoKIyMjIEZTIDUKCiogYGNhbGlwZXJgID0gMAoqIGBjYWxjbG9zZXRgID0gRkFMU0UKKiBgcmF0aW9gID0gMQoqIGByZXBsYWNlYCA9IEZBTFNFCiogOHggb3ZlcnNhbXBsaW5nCgpgYGB7ciBubl9sb2dpdF9mczMsIHdhcm5pbmc9RkFMU0UsIGVycm9yPUZBTFNFLCBlY2hvPUZBTFNFfQojIG5uLmxpbmVhci5sb2dpdC5mczUgPC0gdHJhaW5fbWF0Y2hpdChkZnNbWyc4J11dLCBmczUsIGFkZF9pbnRlcmFjdGlvbnMgPSBGQUxTRSwgcmVwbGFjZSA9IEZBTFNFLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICBjYWxpcGVyID0gMCwgY2FsY2xvc2VzdCA9IEZBTFNFLCByYXRpbyA9IDEsIGRpc3RhbmNlID0gImxpbmVhci5sb2dpdCIpCm5uLmxpbmVhci5sb2dpdC5mczUucHJlZGljdGlvbnMgPC0gZXh0cmFjdF9wcmVkaWN0aW9ucyhubi5saW5lYXIubG9naXQuZnM1JG1hdGNoZWQsIGRmX2JldGFfdmFsKQpubi5saW5lYXIubG9naXQuZnM1LnFxIDwtIGJ1aWxkX3F1YW50aWxlX2RmKGRmX3JlbF92YWwsIG5uLmxpbmVhci5sb2dpdC5mczUucHJlZGljdGlvbnMsIGRmX2JldGFfdmFsKQpubi5saW5lYXIubG9naXQuZnM1LnNjb3JlIDwtIHNjb3JlX21vZGVsKGJ0cywgbm4ubGluZWFyLmxvZ2l0LmZzNS5wcmVkaWN0aW9ucywgZGZfcmVsX3ZhbCkKIyBubi5saW5lYXIubG9naXQuZnM1LnNjb3JlCmBgYAoKUmVzdWx0YW50IHNjb3JlczogYHIgbm4ubGluZWFyLmxvZ2l0LmZzNS5zY29yZWAKCgoqIE51bWJlciBvZiBtYXRjaGVkIEJldGEgdHJhaW5pbmcgc2FtcGxlczogYHIgbnJvdyhubi5saW5lYXIubG9naXQuZnM1JG1hdGNoZWQpYAoqIE51bWJlciBvZiBtYXRjaGVkIEJldGEgdmFsaWRhdGlvbiBzYW1wbGVzOiBgciBucm93KG5uLmxpbmVhci5sb2dpdC5mczUucHJlZGljdGlvbnMpYAoKYGBge3Igbm5fbG9naXRfZnM1X3ZhbF9wbHQsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTI1LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQpwbG90X3ZhbGlkYXRpb25fcmVzdWx0cyhkZl9yZWxfdmFsLCBubi5saW5lYXIubG9naXQuZnM1LnByZWRpY3Rpb25zLCBkZl9iZXRhX3ZhbCkKYGBgCgojIyBOZWFyZXN0LU5laWdoYm9ycyBHQU0sIExvZ2l0CgpIeXBlcnBhcmFtZXRlcnM6IGBoeXBlcnBhcmFtZXRlcl90dW5pbmdfbm5fZ2FtX2xvZ2l0LlJtZGAKCiMjIyBGUyAxCgoqIGBjYWxpcGVyYCA9IDAuNQoqIGBjYWxjbG9zZXRgID0gRkFMU0UKKiBgcmF0aW9gID0gMwoqIGByZXBsYWNlYCA9IEZBTFNFCiogNHggb3ZlcnNhbXBsaW5nCiogSW50ZXJhY3Rpb25zIGFjcm9zcyBjb3ZhcmlhdGVzCgpgYGB7ciBubl9nYW1fZnMxLCB3YXJuaW5nPUZBTFNFLCBlcnJvcj1GQUxTRSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyBubi5nYW0uZnMxIDwtIHRyYWluX21hdGNoaXQoZGZzW1snNCddXSwgZnMxLCBhZGRfaW50ZXJhY3Rpb25zID0gVFJVRSwgcmVwbGFjZSA9IEZBTFNFLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICBjYWxpcGVyID0gMC41LCBjYWxjbG9zZXN0ID0gVFJVRSwgcmF0aW8gPSAzLCBkaXN0YW5jZSA9ICJHQU1sb2dpdCIpCm5uLmdhbS5mczEucHJlZGljdGlvbnMgPC0gZXh0cmFjdF9wcmVkaWN0aW9ucyhubi5nYW0uZnMxJG1hdGNoZWQsIGRmX2JldGFfdmFsKQpubi5nYW0uZnMxLnFxIDwtIGJ1aWxkX3F1YW50aWxlX2RmKGRmX3JlbF92YWwsIG5uLmdhbS5mczEucHJlZGljdGlvbnMsIGRmX2JldGFfdmFsKQpubi5nYW0uZnMxLnNjb3JlIDwtIHNjb3JlX21vZGVsKGJ0cywgbm4uZ2FtLmZzMS5wcmVkaWN0aW9ucywgZGZfcmVsX3ZhbCkKIyBubi5nYW0uZnMxLnNjb3JlCmBgYAoKUmVzdWx0YW50IHNjb3JlczogYHIgbm4uZ2FtLmZzMS5zY29yZWAKCgoqIE51bWJlciBvZiBtYXRjaGVkIEJldGEgdHJhaW5pbmcgc2FtcGxlczogYHIgbnJvdyhubi5nYW0uZnMxJG1hdGNoZWQpYAoqIE51bWJlciBvZiBtYXRjaGVkIEJldGEgdmFsaWRhdGlvbiBzYW1wbGVzOiBgciBucm93KG5uLmdhbS5mczEucHJlZGljdGlvbnMpYAoKYGBge3Igbm5fZ2FtX2ZzMV92YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgbm4uZ2FtLmZzMS5wcmVkaWN0aW9ucywgZGZfYmV0YV92YWwpCmBgYAoKCiMjIyBGUyA1CgoqIGBjYWxpcGVyYCA9IDAKKiBgY2FsY2xvc2V0YCA9IEZBTFNFCiogYHJhdGlvYCA9IDEKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDR4IG92ZXJzYW1wbGluZwoKYGBge3Igbm5fZ2FtX2ZzNSwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0UsIGVjaG89RkFMU0V9CiMgbm4uZ2FtLmZzNSA8LSB0cmFpbl9tYXRjaGl0KGRmc1tbJzQnXV0sIGZzNSwgYWRkX2ludGVyYWN0aW9ucyA9IEZBTFNFLCByZXBsYWNlID0gRkFMU0UsIAojICAgICAgICAgICAgICAgICAgICAgICAgIGNhbGlwZXIgPSAwLCBjYWxjbG9zZXN0ID0gRkFMU0UsIHJhdGlvID0gMSwgZGlzdGFuY2UgPSAiR0FNbG9naXQiKQpubi5nYW0uZnM1LnByZWRpY3Rpb25zIDwtIGV4dHJhY3RfcHJlZGljdGlvbnMobm4uZ2FtLmZzNSRtYXRjaGVkLCBkZl9iZXRhX3ZhbCkKbm4uZ2FtLmZzNS5xcSA8LSBidWlsZF9xdWFudGlsZV9kZihkZl9yZWxfdmFsLCBubi5nYW0uZnM1LnByZWRpY3Rpb25zLCBkZl9iZXRhX3ZhbCkKbm4uZ2FtLmZzNS5zY29yZSA8LSBzY29yZV9tb2RlbChidHMsIG5uLmdhbS5mczUucHJlZGljdGlvbnMsIGRmX3JlbF92YWwpCiMgbm4uZ2FtLmZzNS5zY29yZQpgYGAKClJlc3VsdGFudCBzY29yZXM6IGByIG5uLmdhbS5mczUuc2NvcmVgCgoKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHRyYWluaW5nIHNhbXBsZXM6IGByIG5yb3cobm4uZ2FtLmZzNSRtYXRjaGVkKWAKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHZhbGlkYXRpb24gc2FtcGxlczogYHIgbnJvdyhubi5nYW0uZnM1LnByZWRpY3Rpb25zKWAKCmBgYHtyIG5uX2dhbV9mczVfdmFsX3BsdCwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9MjUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CnBsb3RfdmFsaWRhdGlvbl9yZXN1bHRzKGRmX3JlbF92YWwsIG5uLmdhbS5mczUucHJlZGljdGlvbnMsIGRmX2JldGFfdmFsKQpgYGAKCiMjIE5lYXJlc3QtTmVpZ2hib3JzIFByb2JpdCwgTGluZWFyCgpIeXBlcnBhcmFtZXRlcnM6IGBoeXBlcnBhcmFtZXRlcl90dW5pbmdfbm5fcHJvYml0X2xpbmVhci5SbWRgCgojIyMgRlMgMQoKKiBgY2FsaXBlcmAgPSAwLjQKKiBgY2FsY2xvc2V0YCA9IEZBTFNFCiogYHJhdGlvYCA9IDMKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDR4IG92ZXJzYW1wbGluZwoqIEludGVyYWN0aW9ucyBhY3Jvc3MgY292YXJpYXRlcwoKYGBge3Igbm5fcHJvYml0X2ZzMSwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0UsIGVjaG89RkFMU0V9CiMgbm4ubGluZWFyLnByb2JpdC5mczEgPC0gdHJhaW5fbWF0Y2hpdChkZnNbWyc0J11dLCBmczEsIGFkZF9pbnRlcmFjdGlvbnMgPSBUUlVFLCByZXBsYWNlID0gRkFMU0UsIAojICAgICAgICAgICAgICAgICAgICAgICAgIGNhbGlwZXIgPSAwLjQsIGNhbGNsb3Nlc3QgPSBUUlVFLCByYXRpbyA9IDMsIGRpc3RhbmNlID0gImxpbmVhci5wcm9iaXQiKQpubi5saW5lYXIucHJvYml0LmZzMS5wcmVkaWN0aW9ucyA8LSBleHRyYWN0X3ByZWRpY3Rpb25zKG5uLmxpbmVhci5wcm9iaXQuZnMxJG1hdGNoZWQsIGRmX2JldGFfdmFsKQpubi5saW5lYXIucHJvYml0LmZzMS5xcSA8LSBidWlsZF9xdWFudGlsZV9kZihkZl9yZWxfdmFsLCBubi5saW5lYXIucHJvYml0LmZzMS5wcmVkaWN0aW9ucywgZGZfYmV0YV92YWwpCm5uLmxpbmVhci5wcm9iaXQuZnMxLnNjb3JlIDwtIHNjb3JlX21vZGVsKGJ0cywgbm4ubGluZWFyLnByb2JpdC5mczEucHJlZGljdGlvbnMsIGRmX3JlbF92YWwpCiMgbm4ubGluZWFyLnByb2JpdC5mczEuc2NvcmUKYGBgCgoKUmVzdWx0YW50IHNjb3JlczogYHIgbm4ubGluZWFyLnByb2JpdC5mczEuc2NvcmVgCgoKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHRyYWluaW5nIHNhbXBsZXM6IGByIG5yb3cobm4ubGluZWFyLnByb2JpdC5mczEkbWF0Y2hlZClgCiogTnVtYmVyIG9mIG1hdGNoZWQgQmV0YSB2YWxpZGF0aW9uIHNhbXBsZXM6IGByIG5yb3cobm4ubGluZWFyLnByb2JpdC5mczEucHJlZGljdGlvbnMpYAoKYGBge3Igbm5fcHJvYml0X2ZzMV92YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgbm4ubGluZWFyLnByb2JpdC5mczEucHJlZGljdGlvbnMsIGRmX2JldGFfdmFsKQpgYGAKCiMjIyBGUyA1CgoqIGBjYWxpcGVyYCA9IDAKKiBgY2FsY2xvc2V0YCA9IEZBTFNFCiogYHJhdGlvYCA9IDEKKiBgcmVwbGFjZWAgPSBGQUxTRQoqIDh4IG92ZXJzYW1wbGluZwoKYGBge3Igbm5fcHJvYml0X2ZzNSwgd2FybmluZz1GQUxTRSwgZXJyb3I9RkFMU0UsIGVjaG89RkFMU0V9CiMgbm4ubGluZWFyLnByb2JpdC5mczUgPC0gdHJhaW5fbWF0Y2hpdChkZnNbWyc4J11dLCBmczUsIGFkZF9pbnRlcmFjdGlvbnMgPSBGQUxTRSwgcmVwbGFjZSA9IEZBTFNFLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICBjYWxpcGVyID0gMCwgY2FsY2xvc2VzdCA9IEZBTFNFLCByYXRpbyA9IDEsIGRpc3RhbmNlID0gImxpbmVhci5wcm9iaXQiKQpubi5saW5lYXIucHJvYml0LmZzNS5wcmVkaWN0aW9ucyA8LSBleHRyYWN0X3ByZWRpY3Rpb25zKG5uLmxpbmVhci5wcm9iaXQuZnM1JG1hdGNoZWQsIGRmX2JldGFfdmFsKQpubi5saW5lYXIucHJvYml0LmZzNS5xcSA8LSBidWlsZF9xdWFudGlsZV9kZihkZl9yZWxfdmFsLCBubi5saW5lYXIucHJvYml0LmZzNS5wcmVkaWN0aW9ucywgZGZfYmV0YV92YWwpCm5uLmxpbmVhci5wcm9iaXQuZnM1LnNjb3JlIDwtIHNjb3JlX21vZGVsKGJ0cywgbm4ubGluZWFyLnByb2JpdC5mczUucHJlZGljdGlvbnMsIGRmX3JlbF92YWwpCiMgbm4ubGluZWFyLnByb2JpdC5mczUuc2NvcmUKYGBgCgpSZXN1bHRhbnQgc2NvcmVzOiBgciBubi5saW5lYXIucHJvYml0LmZzNS5zY29yZWAKCgoqIE51bWJlciBvZiBtYXRjaGVkIEJldGEgdHJhaW5pbmcgc2FtcGxlczogYHIgbnJvdyhubi5saW5lYXIucHJvYml0LmZzNSRtYXRjaGVkKWAKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHZhbGlkYXRpb24gc2FtcGxlczogYHIgbnJvdyhubi5saW5lYXIucHJvYml0LmZzNS5wcmVkaWN0aW9ucylgCgpgYGB7ciBubl9wcm9iaXRfZnM1X3ZhbF9wbHQsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTI1LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQpwbG90X3ZhbGlkYXRpb25fcmVzdWx0cyhkZl9yZWxfdmFsLCBubi5saW5lYXIucHJvYml0LmZzNS5wcmVkaWN0aW9ucywgZGZfYmV0YV92YWwpCmBgYAoKIyMgR2VuZXRpYyBNYXRjaGluZwoKRHVlIHRvIHRoZSB2ZXJ5IGxvbmcgdHJhaW5pbmcgdGltZXMgb2YgZ2VuZXRpYyBtYXRjaGluZywgZmVhdHVyZSBzZWxlY3Rpb24gYW5kIGh5cGVycGFyYW1ldGVyIHR1bmluZyB3ZXJlIG5vdCBwZXJmb3JtZWQgdXNpbmcgYm9vdHN0cmFwIHNhbXBlcy4gVGhlIG1vZGVscyB3ZXJlIHByZXZpb3VzbHkgdHJhaW5lZCB1c2luZyB0aGUgZnVsbCB0cmFpbmluZyBzYW1wbGUuIFRoZSByZXN1bHRhbnQgbWF0Y2ggYmV0YSBzdWJzZXRzIHdlcmUgc2VyaWFsaXplZCBhbmQgdXBsb2FkZWQgdG8gR0NQLgoKVHJhaW5lZDogYGZlYXR1cmVfc2VsZWN0aW9uX2dlbm1hdGNoX2xpbmVhci5SbWRgCgpgYGB7ciBnZW5tYXRjaCwgZWNobz1GQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmZpbGVfbmFtZSA9ICdmZWF0dXJlX3NlbGVjdGlvbl9nZW5tYXRjaF8uUkRhdGEnCmltYWdlX2ZpbGVfcGF0aCA9IGZpbGUucGF0aCgnZGF0YScsIGZpbGVfbmFtZSkKCiMgUHVsbCBmcm9tIEdDUCBpZiBuZWNlc3NhcnkKaWYgKCFmaWxlLmV4aXN0cyhpbWFnZV9maWxlX3BhdGgpKXsKICBTeXMuc2V0ZW52KCJHQ1NfREVGQVVMVF9CVUNLRVQiID0gIm1vei1meC1kZXYtc3ViYmV0YSIsCiAgICAgICAgICAgIkdDU19BVVRIX0ZJTEUiID0gIm1vei1meC1kZXYtY2Rvd2h5Z2x1bmQtc3ViQmV0YS03ODhmOGYwZDQ2MjcuanNvbiIpCiAgbGlicmFyeShnb29nbGVDbG91ZFN0b3JhZ2VSKQogIGdjc19nZXRfb2JqZWN0KGZpbGUucGF0aCgnZGF0YScsICdtaWxlc3RvbmUyJywgZmlsZV9uYW1lKSwgc2F2ZVRvRGlzayA9IGltYWdlX2ZpbGVfcGF0aCwgb3ZlcndyaXRlID0gVFJVRSkKCn0KbG9hZChpbWFnZV9maWxlX3BhdGgpCmBgYAoKIyMjIEZTIDEKCmBgYHtyIGdlbm1hdGNoX2ZzMSwgZWNobz1GQUxTRX0KZ2VubWF0Y2guZnMxLm1hdGNoZWQgPC0gZGZfdHJhaW5fZ2VuW2ZzMV9yZXN1bHRzJG1hdGNoZXMkaW5kZXguY29udHJvbCxdCmdlbm1hdGNoLmZzMS5wcmVkaWN0aW9ucyA8LSBleHRyYWN0X3ByZWRpY3Rpb25zKGdlbm1hdGNoLmZzMS5tYXRjaGVkLCBkZl9iZXRhX3ZhbCkKZ2VubWF0Y2guZnMxLnFxIDwtIGJ1aWxkX3F1YW50aWxlX2RmKGRmX3JlbF92YWwsIGdlbm1hdGNoLmZzMS5wcmVkaWN0aW9ucywgZGZfYmV0YV92YWwpCmdlbm1hdGNoLmZzMS5zY29yZSA8LSBzY29yZV9tb2RlbChidHMsIGdlbm1hdGNoLmZzMS5wcmVkaWN0aW9ucywgZGZfcmVsX3ZhbCkKIyBnZW5tYXRjaC5mczEuc2NvcmUKYGBgCgpSZXN1bHRhbnQgc2NvcmVzOiBgciBnZW5tYXRjaC5mczEuc2NvcmVgCgoKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHRyYWluaW5nIHNhbXBsZXM6IGByIGxlbmd0aCh1bmlxdWUoZ2VubWF0Y2guZnMxLm1hdGNoZWQkY2xpZW50X2lkKSlgCiogTnVtYmVyIG9mIG1hdGNoZWQgQmV0YSB2YWxpZGF0aW9uIHNhbXBsZXM6IGByIG5yb3coZ2VubWF0Y2guZnMxLnByZWRpY3Rpb25zKWAKCmBgYHtyIGdlbm1hdGNoX2ZzMV92YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgZ2VubWF0Y2guZnMxLnByZWRpY3Rpb25zLCBkZl9iZXRhX3ZhbCkKYGBgCgojIyMjIFBvcC5TaXplIDUwMAoKYGBge3IgZ2VubWF0Y2hfZnMxXzUwMCwgZWNobz1GQUxTRX0KZ2VubWF0Y2guZnMxLjUwMC5tYXRjaGVkIDwtIGRmX3RyYWluX2dlbltmczFfNTAwX3Jlc3VsdHMkbWF0Y2hlcyRpbmRleC5jb250cm9sLF0KZ2VubWF0Y2guZnMxLjUwMC5wcmVkaWN0aW9ucyA8LSBleHRyYWN0X3ByZWRpY3Rpb25zKGdlbm1hdGNoLmZzMS41MDAubWF0Y2hlZCwgZGZfYmV0YV92YWwpCmdlbm1hdGNoLmZzMS41MDAucXEgPC0gYnVpbGRfcXVhbnRpbGVfZGYoZGZfcmVsX3ZhbCwgZ2VubWF0Y2guZnMxLjUwMC5wcmVkaWN0aW9ucywgZGZfYmV0YV92YWwpCmdlbm1hdGNoLmZzMS41MDAuc2NvcmUgPC0gc2NvcmVfbW9kZWwoYnRzLCBnZW5tYXRjaC5mczEuNTAwLnByZWRpY3Rpb25zLCBkZl9yZWxfdmFsKQojIGdlbm1hdGNoLmZzMS41MDAuc2NvcmUKYGBgCgpSZXN1bHRhbnQgc2NvcmVzOiBgciBnZW5tYXRjaC5mczEuNTAwLnNjb3JlYAoKCiogTnVtYmVyIG9mIG1hdGNoZWQgQmV0YSB0cmFpbmluZyBzYW1wbGVzOiBgciBsZW5ndGgodW5pcXVlKGdlbm1hdGNoLmZzMS41MDAubWF0Y2hlZCRjbGllbnRfaWQpKWAKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHZhbGlkYXRpb24gc2FtcGxlczogYHIgbnJvdyhnZW5tYXRjaC5mczEuNTAwLnByZWRpY3Rpb25zKWAKCmBgYHtyIGdlbm1hdGNoX2ZzMS41MDBfdmFsX3BsdCwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9MjUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CnBsb3RfdmFsaWRhdGlvbl9yZXN1bHRzKGRmX3JlbF92YWwsIGdlbm1hdGNoLmZzMS41MDAucHJlZGljdGlvbnMsIGRmX2JldGFfdmFsKQpgYGAKCgojIyMgRlMgMwoKYGBge3IgZ2VubWF0Y2hfZnMzLCBlY2hvPUZBTFNFfQpnZW5tYXRjaC5mczMubWF0Y2hlZCA8LSBkZl90cmFpbl9nZW5bZnMzX3Jlc3VsdHMkbWF0Y2hlcyRpbmRleC5jb250cm9sLF0KZ2VubWF0Y2guZnMzLnByZWRpY3Rpb25zIDwtIGV4dHJhY3RfcHJlZGljdGlvbnMoZ2VubWF0Y2guZnMzLm1hdGNoZWQsIGRmX2JldGFfdmFsKQpnZW5tYXRjaC5mczMucXEgPC0gYnVpbGRfcXVhbnRpbGVfZGYoZGZfcmVsX3ZhbCwgZ2VubWF0Y2guZnMzLnByZWRpY3Rpb25zLCBkZl9iZXRhX3ZhbCkKZ2VubWF0Y2guZnMzLnNjb3JlIDwtIHNjb3JlX21vZGVsKGJ0cywgZ2VubWF0Y2guZnMzLnByZWRpY3Rpb25zLCBkZl9yZWxfdmFsKQojIGdlbm1hdGNoLmZzMy5zY29yZQpgYGAKClJlc3VsdGFudCBzY29yZXM6IGByIGdlbm1hdGNoLmZzMy5zY29yZWAKCgoqIE51bWJlciBvZiBtYXRjaGVkIEJldGEgdHJhaW5pbmcgc2FtcGxlczogYHIgbGVuZ3RoKHVuaXF1ZShnZW5tYXRjaC5mczMubWF0Y2hlZCRjbGllbnRfaWQpKWAKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHZhbGlkYXRpb24gc2FtcGxlczogYHIgbnJvdyhnZW5tYXRjaC5mczMucHJlZGljdGlvbnMpYAoKYGBge3IgZ2VubWF0Y2hfZnMzX3ZhbF9wbHQsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTI1LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQpwbG90X3ZhbGlkYXRpb25fcmVzdWx0cyhkZl9yZWxfdmFsLCBnZW5tYXRjaC5mczMucHJlZGljdGlvbnMsIGRmX2JldGFfdmFsKQpgYGAKCiMjIyBGUyA1CgpgYGB7ciBnZW5tYXRjaF9mczUsIGVjaG89RkFMU0V9Cmdlbm1hdGNoLmZzNS5tYXRjaGVkIDwtIGRmX3RyYWluX2dlbltmczVfcmVzdWx0cyRtYXRjaGVzJGluZGV4LmNvbnRyb2wsXQpnZW5tYXRjaC5mczUucHJlZGljdGlvbnMgPC0gZXh0cmFjdF9wcmVkaWN0aW9ucyhnZW5tYXRjaC5mczUubWF0Y2hlZCwgZGZfYmV0YV92YWwpCmdlbm1hdGNoLmZzNS5xcSA8LSBidWlsZF9xdWFudGlsZV9kZihkZl9yZWxfdmFsLCBnZW5tYXRjaC5mczUucHJlZGljdGlvbnMsIGRmX2JldGFfdmFsKQpnZW5tYXRjaC5mczUuc2NvcmUgPC0gc2NvcmVfbW9kZWwoYnRzLCBnZW5tYXRjaC5mczUucHJlZGljdGlvbnMsIGRmX3JlbF92YWwpCiNnZW5tYXRjaC5mczUuc2NvcmUKYGBgCgpSZXN1bHRhbnQgc2NvcmVzOiBgciBnZW5tYXRjaC5mczUuc2NvcmVgCgoKKiBOdW1iZXIgb2YgbWF0Y2hlZCBCZXRhIHRyYWluaW5nIHNhbXBsZXM6IGByIGxlbmd0aCh1bmlxdWUoZ2VubWF0Y2guZnM1Lm1hdGNoZWQkY2xpZW50X2lkKSlgCiogTnVtYmVyIG9mIG1hdGNoZWQgQmV0YSB2YWxpZGF0aW9uIHNhbXBsZXM6IGByIG5yb3coZ2VubWF0Y2guZnM1LnByZWRpY3Rpb25zKWAKCmBgYHtyIGdlbm1hdGNoX2ZzNV92YWxfcGx0LCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD0yNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KcGxvdF92YWxpZGF0aW9uX3Jlc3VsdHMoZGZfcmVsX3ZhbCwgZ2VubWF0Y2guZnM1LnByZWRpY3Rpb25zLCBkZl9iZXRhX3ZhbCkKYGBgCgojIFJlc3VsdHMKCk5lYXJlc3QtTmVpZ2hib3JzIFByb2JpdCwgTGluZWFyIHByb2R1Y2VzIHRoZSBvcHRpbWFsIG1hdGNoaW5nIHJlc3VsdHMuIEl0IHlpZWxkcyB0aGUgbG93ZXN0IHNjb3Jlcywgd2l0aG91dCBkcm9wcGluZyBsYXJnZSBzYW1wbGUgb2YgdGhlIEJldGEgcHJvZmlsZXMuIFVuc3VycHJpc2luZ2x5LCBtYXRjaGluZyBkaXJlY3RseSwgYW5kIG9ubHksIG9uIHRoZSBwZXJmb3JtYW5jZSBtZXRyaWNzIHlpZWxkcyB0aGUgYmVzdCByZXN1bHRzLiBJbnRlcmVzdGluZ2x5LCBpdCBhcHBlYXJzIHRoYXQgaW50ZXJhY3Rpb25zIGFjcm9zcyBjb3ZhcmlhdGVzIGdyZWF0bHkgaGVscC4gRm9ydHVuYXRlbHksIHRoaXMgbW9kZWwgYXJjaGl0ZWN0dXJlIGlzIHJlbGF0aXZlbHkgY2hlYXAgZnJvbSBhIGNvbXB1dGF0aW9uYWwgcGVyc3BlY3RpdmUuIAoKQmFsYW5jaW5nIG9uIGFkZGl0aW9uYWwgY292YXJpYXRlcyAoZS5nLCBGUzMgb3IgRlM1KSwgb3V0c2lkZSBvZiB0aGUgcGVyZm9ybWFuY2UgbWV0cmljcywgeWllbGRzIHN1Yi1vcHRpbWFsIHJlc3VsdHMuIE5lYXJlc3QtTmVpZ2hib3JzIFByb2JpdCwgTGluZWFyIGFnYWluIHlpZWxkcyB0aGUgbG93ZXN0IHNjb3JlIChgciBubi5saW5lYXIucHJvYml0LmZzNS5zY29yZWApOyBob3dldmVyLCB0aGlzIGlzIGF0IHRoZSBleHBlbnNlIG9mIGEgZ3JlYXRseSByZWR1Y2VkIEJldGEgc2FtcGxlcyBzaXplIChgciBucm93KG5uLmxpbmVhci5wcm9iaXQuZnM1LnByZWRpY3Rpb25zKWApLiBHZW5ldGljIG1hdGNoaW5nIGlzIGFibGUgdG8gcmV0YWluIGEgbXVjaCBsYXJnZXIgc2FtcGxlIHNpemUgKGByIG5yb3coZ2VubWF0Y2guZnMzLnByZWRpY3Rpb25zKWApLCBhdCB0aGUgZXhwZW5zZSBvZiBncmVhdGVyIGVycm9yIChgciBnZW5tYXRjaC5mczMuc2NvcmVgKS4gCgoqKkNvbmNsdXNpb24qKjogT3B0aW1hbCBtb2RlbCBmcmFtZXdvcmsKCiogbWF0Y2hpbmcgYWxnb3JpdGhtOiBOZWFyZXN0LU5laWdoYm9ycyBQcm9iaXQsIExpbmVhcgoqIGZlYXR1cmVzOiBPbmx5IHBlcmZvcm1hbmNlIG1ldHJpY3MKKiBoeXBlcnBhcmFtZXRlcnM6IAogICAgKiBgY2FsaXBlcmAgPSAwLjQKICAgICogYGNhbGNsb3NldGAgPSBGQUxTRQogICAgKiBgcmF0aW9gID0gMwogICAgKiBgcmVwbGFjZWAgPSBGQUxTRQogICAgKiA0eCBvdmVyc2FtcGxpbmcKICAgICogSW50ZXJhY3Rpb25zIGFjcm9zcyBjb3ZhcmlhdGVzCgoKIyBQdXNoIHRvIEdDUAoKVGhlIGJvb3N0cmFwIHJlcGxpY2F0ZXMgZm9yIHZhbGlkYXRpb24sIG92ZXJzYW1wbGVkIHRyYWluaW5pZyBkYXRhc2V0cywgcmVzdWx0YW50IHF1YW50aWxlIGNhbGN1bGF0aW9ucywgYW5kIG1hdGNoZWQgZGF0YXNldHMgYXJlIHNhdmVkIHRvIGFuIFIgaW1hZ2UuIFRoZW4gdXBsb2FkZWQgdG8gdGhlIHByb2plY3QgR0NQIGJ1Y2tldC4KCmBgYHtyIHNlcmlhbGl6ZV9nY3AsIGVjaG89RkFMU0V9CnJlc3VsdHNfZmlsZV9uYW1lID0gJ3ZhbGlkYXRpb25fcmVzdWx0c18yMDE5MTEyMS5SRGF0YScKcmVzdWx0c19maWxlX3BhdGggPSBmaWxlLnBhdGgoJ2RhdGEnLCByZXN1bHRzX2ZpbGVfbmFtZSkKCm9iamVjdHMgPC0gYygKICBscyhwYXR0ZXJuID0gJ2dlbm1hdGNoJyksCiAgbHMocGF0dGVybiA9ICdubicpLAogIGxzKHBhdHRlcm4gPSAnXmJ0cyQnKSwKICBscyhwYXR0ZXJuID0gJ15kZnMkJykKKQoKc2F2ZShsaXN0ID0gb2JqZWN0cywgZmlsZSA9IHJlc3VsdHNfZmlsZV9wYXRoKQpgYGAKCgpgYGB7ciBwdXNoX3RvX2djcCwgZWNobz1GQUxTRX0KZ2NzX2ZpbGVfcGF0aCA8LSBmaWxlLnBhdGgoJ2RhdGEnLCAnbWlsZXN0b25lMicsIHJlc3VsdHNfZmlsZV9uYW1lKQoKU3lzLnNldGVudigiR0NTX0RFRkFVTFRfQlVDS0VUIiA9ICJtb3otZngtZGV2LXN1YmJldGEiLAogICAgICAgICAgICJHQ1NfQVVUSF9GSUxFIiA9ICJtb3otZngtZGV2LWNkb3doeWdsdW5kLXN1YkJldGEtNzg4ZjhmMGQ0NjI3Lmpzb24iKQoKbGlicmFyeShnb29nbGVDbG91ZFN0b3JhZ2VSKQoKcHJval9maWxlcyA9IGdjc19saXN0X29iamVjdHMoKQoKaWYgKGdjc19maWxlX3BhdGggJWluJSBwcm9qX2ZpbGVzJG5hbWUpIHsKICBwcmludChwYXN0ZSgnUHJldmlvdXNseSB0cmFpbmVkIHJlc3VsdHMgYWxyZWFkeSBleGlzdDonLCBnY3NfZmlsZV9wYXRoKSkKfSBlbHNlIHsKICBwcmludChwYXN0ZSgnVXBsb2FkaW5nIHZhbGlkYXRpb24gcmVzdWx0cyB0byBHQ1A6JywgZ2NzX2ZpbGVfcGF0aCkpCiAgdXBsb2FkX3RyeSA8LSBnY3NfdXBsb2FkKHJlc3VsdHNfZmlsZV9wYXRoLCBuYW1lID0gZ2NzX2ZpbGVfcGF0aCkKICB1cGxvYWRfdHJ5Cn0KYGBgCgoKCg==